﻿using System;
using System.Globalization;
using QAToolsAddin.Comparer;
using QAToolsAddin.Components;
using QAToolsAddin.GenericDialogs;
using Excel = Microsoft.Office.Interop.Excel;

namespace QAToolsAddin.CustomDialogs {
    public partial class FrmCombine : ExcelForm {
        private readonly Excel.Application _excelapp;
        private Excel.Workbook _workbook;
        private Excel.Worksheet _worksheet;
        private readonly object Missing = Type.Missing;

        public FrmCombine(Excel.Application excelapp, CultureInfo culture)
            : base(excelapp) {
            InitializeComponent();
            _excelapp = excelapp;
            ctrlWorkbookA.SelectionChanged += ctrlWorkbookA_SelectedIndexChanged;
            ctrlWorkbookB.SelectionChanged += ctrlWorkbookB_SelectedIndexChanged;
            ctrlRefA.WindowActivate += ctrlWorkbookA.SetWindow;
            ctrlRefB.WindowActivate += ctrlWorkbookB.SetWindow;
        }

        private void LoadSettings() {
            ctrlIgnoreCase.Checked = (bool)Settings.GetParam(this, "ctrlIgnoreCase", false);
            ctrlOptNewSheet.Checked = (bool)Settings.GetParam(this, "ctrlOptNewSheet", true);
            ctrlOptSelection.Checked = (bool)Settings.GetParam(this, "ctrlOptSelection", true);
            ctrlInteriorColor.SelectedColor = (int)Settings.GetParam(this, "ctrlInteriorColor", 0xFF);
            ctrlFontColor.SelectedColor = (int)Settings.GetParam(this, "ctrlFontColor", 0xFFFFFF);
        }

        private void SaveSettings() {
            Settings.SetParam(this, "ctrlIgnoreCase", ctrlIgnoreCase.Checked);
            Settings.SetParam(this, "ctrlOptNewSheet", ctrlOptNewSheet.Checked);
            Settings.SetParam(this, "ctrlOptSelection", ctrlOptSelection.Checked);
            Settings.SetParam(this, "ctrlInteriorColor", ctrlInteriorColor.SelectedColor);
            Settings.SetParam(this, "ctrlFontColor", ctrlFontColor.SelectedColor);
        }

        private void FrmCombine_Load(object sender, EventArgs e) {
            Excel.Range sel1 = null, sel2 = null;
            _workbook = _excelapp.ActiveWorkbook;
            _worksheet = (Excel.Worksheet)_excelapp.ActiveSheet;
            if (_workbook == null || _worksheet == null)
                throw new InputError("Open a workbook first!");

            SuspendLayout();
            LoadSettings();

            int nbSelectedSheet = _excelapp.ActiveWindow.SelectedSheets.Count;
            if (nbSelectedSheet < 3) {
                if (nbSelectedSheet == 1) {
                    var selection = _excelapp.ActiveWindow.RangeSelection;
                    int nbarea = selection.Areas.Count;
                    if (nbarea == 1) {
                        if (selection.Worksheet.UsedRange.Cells.Count != 1)
                            sel1 = Utils.ExtendRange(selection);
                    } else {
                        sel1 = Utils.ExtendRange(selection.Areas[1]);
                        sel2 = Utils.ExtendRange(selection.Areas[2]);
                    }
                } else {
                    sel1 = ((Excel.Worksheet)_excelapp.ActiveWindow.SelectedSheets[1]).UsedRange;
                    sel2 = ((Excel.Worksheet)_excelapp.ActiveWindow.SelectedSheets[2]).UsedRange;
                }

                ctrlWithHeadersA.Checked = Utils.RangeHasHeaders(sel1);
                ctrlWithHeadersB.Checked = Utils.RangeHasHeaders(sel2);
                if (sel1 != null)
                    ctrlRefA.Range = sel1;
                if (sel2 != null)
                    ctrlRefB.Range = sel2;

            } else {
                MsgBox.ShowError(_excelapp, "You can't select more than 2 sheets!");
            }
            ResumeLayout();
        }

        private void UpdateKeysA_Delegate(object sender, EventArgs e) {
            ctrlKeysA.SetColumnSelector(ctrlRefA.Range, ctrlWithHeadersA.Checked);
        }

        private void UpdateKeysB_Delegate(object sender, EventArgs e) {
            ctrlKeysB.SetColumnSelector(ctrlRefB.Range, ctrlWithHeadersB.Checked);
        }

        private void ctrlWithHeaders_CheckedChanged(object sender, EventArgs e) {
            ctrlKeysA.SetColumnSelector(ctrlRefA.Range, ctrlWithHeadersA.Checked);
            ctrlKeysB.SetColumnSelector(ctrlRefB.Range, ctrlWithHeadersB.Checked);
        }

        private void btCombine_Click(object sender, EventArgs e) {
            try {
                Utils.SwitchExcelToIdle(_excelapp, false);
                RunCombine();
            } catch (InputError ex) {
                MsgBox.ShowError(_excelapp, ex.Message);
            } finally {
                Utils.SwitchExcelToIdle(_excelapp, true);
            }
        }

        private void btClose_Click(object sender, EventArgs e) {
            Close();
        }

        private void RunCombine() {
            if (ctrlRefA.IsEmpty || ctrlRefA.Range == null) throw new InputError("Selection 1 is empty or incorrect.");
            if (ctrlRefB.IsEmpty || ctrlRefB == null) throw new InputError("Selection 2 is empty or incorrect.");

            //Shrink to used range
            Excel.Range rgSel1 = _excelapp.Intersect(ctrlRefA.Range, ctrlRefA.Range.Worksheet.UsedRange);
            if (rgSel1 == null) throw new InputError("Selection 1 is incorrect!");
            Excel.Range rgSel2 = _excelapp.Intersect(ctrlRefB.Range, ctrlRefB.Range.Worksheet.UsedRange);
            if (rgSel2 == null) throw new InputError("Selection 2 is incorrect!");

            //Define data range
            int nbRow1 = rgSel1.Rows.Count, nbCol1 = rgSel1.Columns.Count;
            int nbRow2 = rgSel2.Rows.Count, nbCol2 = rgSel2.Columns.Count;
            int offsetRow1 = ctrlWithHeadersA.Checked ? 1 : 0;
            int offsetRow2 = ctrlWithHeadersB.Checked ? 1 : 0;
            Excel.Range rgData1 = rgSel1.Offset[offsetRow1, Type.Missing].Resize[nbRow1 - offsetRow1, Type.Missing];
            Excel.Range rgData2 = rgSel2.Offset[offsetRow2, Type.Missing].Resize[nbRow2 - offsetRow2, Type.Missing];
            if (rgData1 == null) throw new InputError("Selection 1 is empty.");
            if (rgData2 == null) throw new InputError("Selection 2 is empty.");
            if (rgData1.Cells.Count < 2) throw new InputError("Selection 1 must contain at least 2 cells.");
            if (rgData2.Cells.Count < 2) throw new InputError("Selection 2 must contain at least 2 cells.");

            //read data
            int[] keysColA = ctrlKeysA.CheckedIds;
            int[] keysColB = ctrlKeysB.CheckedIds;
            if (ctrlKeysA.IsEmpty) throw new InputError("Rows identifiers for selection 1 are missing.");
            if (ctrlKeysB.IsEmpty) throw new InputError("Rows identifiers for selection 2 are missing.");
            if (keysColA.Length != keysColB.Length) throw new InputError("Number of identifiers is different.");
            object[,] data1 = Utils.GetDataFromRange(rgData1, false);
            object[,] data2 = Utils.GetDataFromRange(rgData2, false);
            object[,] titles1 = Utils.GetTitlesFromRange(rgSel1, offsetRow1 != 0);
            object[,] titles2 = Utils.GetTitlesFromRange(rgSel2, offsetRow2 != 0);
            int[] valColA = Utils.GetVisibleColumnsPosition(rgData1, null, keysColA);
            int[] valColB = Utils.GetVisibleColumnsPosition(rgData2, null, keysColB);
            object[] formats1 = Utils.GetColumnsFormating(rgData1);
            object[] formats2 = Utils.GetColumnsFormating(rgData2);

            //Initilise compare class and save seeting
            Excel._Worksheet worksheet = null;
            var compare = new Compare { IgnoreCase = ctrlIgnoreCase.Checked };
            SaveSettings();


            RowSet dataCols = compare.CombineColumns(ref data1, ref data2, ref titles1, ref titles2, keysColA, keysColB, valColA, valColB);

            Excel.Range rgTarget = null;
            if (ctrlOptSelection.Checked) {
                worksheet = (Excel.Worksheet)_workbook.ActiveSheet;
                rgSel1.Clear();
                rgSel2.Clear();
                rgSel1.EntireRow.Hidden = false;
                rgSel2.EntireRow.Hidden = false;
                rgSel1.EntireColumn.Hidden = false;
                rgSel2.EntireColumn.Hidden = false;
                rgTarget = rgData1.Resize[dataCols.RowLen, dataCols.ColLen];
            } else if (ctrlOptNewSheet.Checked) {
                worksheet = (Excel.Worksheet)_workbook.Sheets.Add(Missing, _workbook.Sheets[_workbook.Sheets.Count], Missing, Missing);
                rgTarget = ((Excel.Range)worksheet.Cells[1 + offsetRow1, 1]).Resize[dataCols.RowLen, dataCols.ColLen];
                worksheet.Activate();
            }

            //Copy formats
            int c = 0;
            foreach (int i in keysColA)
                ((Excel.Range)rgTarget.Columns[++c]).NumberFormat = formats1[i - 1];
            foreach (int i in valColA)
                ((Excel.Range)rgTarget.Columns[++c]).NumberFormat = formats1[i - 1];
            foreach (int i in valColB)
                ((Excel.Range)rgTarget.Columns[++c]).NumberFormat = formats2[i - 1];

            //copy data
            if (ctrlWithHeadersA.Checked)
                Utils.AddTitlesToRange((Excel.Range)rgTarget.Rows[0], dataCols.Titles);
            Utils.AddDataToRange(rgTarget, dataCols.Data);
            Utils.AddTagsToRange(rgTarget, dataCols.Diff, ctrlInteriorColor.SelectedColor, ctrlFontColor.SelectedColor);

        }

        private void ctrlRef_Escape(object sender, EventArgs e) {
            btClose.Focus();
        }

        private void ctrlWorkbookA_SelectedIndexChanged(object sender, EventArgs e) {
            var window = ctrlWorkbookA.SelectedWindow;
            ctrlRefA.Workbook = (Excel.Workbook)window.Parent;
            ctrlKeysA.Items = null;
            window.Activate();
            ctrlRefA.Focus();
        }

        private void ctrlWorkbookB_SelectedIndexChanged(object sender, EventArgs e) {
            var window = ctrlWorkbookB.SelectedWindow;
            ctrlRefB.Workbook = (Excel.Workbook)window.Parent;
            ctrlKeysB.Items = null;
            window.Activate();
            ctrlRefB.Focus();
        }


    }
}
